1 module targets.ios; 2 import common_macos; 3 import commons; 4 import global_opts; 5 6 enum DefaultIphone = "iPhone 14"; 7 enum DefaultIphoneOS = "16"; 8 enum TARGET_TYPE = "simulator"; 9 string getTargetArchitecture(bool isSimulator) 10 { 11 import hconfigs; 12 if(isSimulator) 13 { 14 static if(isARM) 15 return "arm64"; 16 else 17 return "x86_64"; 18 } 19 return "arm64"; 20 } 21 22 string getExtraCommand(string type) 23 { 24 if(type == "simulator") return " -sdk iphonesimulator "; 25 return " -sdk iphoneos"; 26 } 27 28 29 /** 30 * Supports up to 99.9 31 */ 32 int iosVersionToInt(string v) 33 { 34 ulong power = 2; 35 int ret = 0; 36 foreach(ch; v) 37 { 38 if(ch == '.') 39 continue; 40 ret+= (ch - '0') * 10^^power; 41 power--; 42 } 43 return ret; 44 } 45 46 string versionFromInt(long input) 47 { 48 ulong value = input; 49 char[] v; 50 51 do 52 { 53 char rest = cast(char)(value % 10); 54 v~= '0' + rest; 55 value -= rest; 56 value/= 10; 57 } 58 while(value >= 1); 59 if(!v.length) 60 return null; 61 62 63 for(int i = 0; i < v.length / 2; i++) 64 { 65 char first = v[i]; 66 int last = i + (cast(int)v.length - 1); 67 v[i] = v[last]; 68 v[last] = first; 69 } 70 if(v[$-1] != '0') 71 return cast(string)v[0..$-1] ~ "." ~ v[$-1]; 72 else 73 return cast(string)v[0..$-1]; 74 } 75 76 string trimCharacters(string input) 77 { 78 import std.string; 79 import std.ascii; 80 input = input.chomp; 81 int lower = 0; 82 int upper = (cast(int)input.length - 1); 83 if(!input.length) 84 return input; 85 while(lower < upper && !input[lower].isDigit) 86 lower++; 87 while(upper > lower && !input[upper].isDigit) 88 upper--; 89 if(upper - lower != 0) 90 return input[lower..upper+1]; 91 return null; 92 } 93 94 unittest 95 { 96 assert(trimCharacters("16e") == "16"); 97 assert(iosVersionToInt("18.2") == 182); 98 assert(versionFromInt(182) == "18.2"); 99 } 100 101 private string getDestination() 102 { 103 // return "-destination 'generic/platform=iOS' "; 104 return "-destination 'platform=iOS Simulator,name="~ 105 configs["iosDevice"].str ~ 106 ",OS=" ~configs["iosVersions"].array[0].integer.versionFromInt ~ "' "; 107 } 108 private string getIosTriple(string arch, bool isSimulator) 109 { 110 import hconfigs; 111 static if(isARM) 112 { 113 if(isSimulator) 114 return "arm64-apple-ios14.0-simulator"; 115 } 116 return arch~"-apple-ios14.0"; 117 } 118 119 string getIosLibFolder(bool isSimulator) 120 { 121 version(X86_64) 122 { 123 if(isSimulator) 124 return "ios-x86_64"; 125 } 126 else 127 { 128 if(isSimulator) 129 return "ios-arm64-simulator"; 130 } 131 return "ios-arm64"; 132 } 133 ChoiceResult putInstalledDeviceInformation(ref Terminal t) 134 { 135 import std.process; 136 import std.json; 137 import commons; 138 ///A way to get which devices are registered in the user PC 139 auto res = executeShell("xcrun simctl list runtimes --json"); 140 if(res.status) 141 { 142 t.writelnError("No iOS runtime found? Please try opening XCode and installing the iOS runtime."); 143 return ChoiceResult.Error; 144 } 145 JSONValue json = parseJSON(res.output); 146 int[] iosVersions; 147 int bestVersion = 0; 148 149 foreach(JSONValue v; json["runtimes"].array) 150 { 151 import std.string; 152 import std.conv; 153 if(!v["isAvailable"].boolean || v["platform"].str != "iOS") 154 continue; 155 iosVersions~= v["version"].str.iosVersionToInt; 156 157 foreach(JSONValue device; v["supportedDeviceTypes"].array) 158 { 159 if(device["productFamily"].str != "iPhone" || !device["identifier"].str.startsWith("com.apple.CoreSimulator.SimDeviceType")) 160 continue; 161 string[] nameParts = device["name"].str.split(" "); 162 if(nameParts.length < 2) 163 continue; 164 nameParts[1] = nameParts[1].trimCharacters; 165 if(nameParts[1].length) 166 { 167 int currVersion = nameParts[1].to!int; 168 bestVersion = currVersion > bestVersion ? currVersion : bestVersion; 169 } 170 } 171 } 172 import std.algorithm.sorting; 173 import std.array; 174 import std.conv; 175 176 configs["iosVersions"] = JSONValue(iosVersions.sort.array); 177 configs["iosDevice"] = JSONValue("iPhone " ~ bestVersion.to!string); 178 updateConfigFile(); 179 180 return ChoiceResult.Continue; 181 } 182 183 ChoiceResult prepareiOS(Choice* c, ref Terminal t, ref RealTimeConsoleInput input, in CompilationOptions cOpts) 184 { 185 bool isSimulator = TARGET_TYPE == "simulator"; 186 string arch = getTargetArchitecture(isSimulator); 187 prepareAppleOSBase(c,t,input); 188 189 string out_extraLinkerFlags; 190 setupPerCompiler(t, "ldc2", getIosLibFolder(isSimulator), out_extraLinkerFlags); 191 injectLinkerFlagsOnXcode(t, input, out_extraLinkerFlags); 192 if(!("lastUser" in configs)) 193 { 194 configs["lastUser"] = environment["USER"]; 195 configs["firstiOSRun"] = true; 196 } 197 if(environment["USER"] != configs["lastUser"].str) 198 { 199 configs["firstiOSRun"] = true; 200 } 201 if("iosDevice" !in configs || "iosVersions" !in configs || !configs["iosVersions"].array.length) 202 putInstalledDeviceInformation(t); 203 204 appleClean = configs["firstiOSRun"].boolean; 205 206 string codeSignCommand = getCodeSignCommand(t); 207 string extraCommands = getExtraCommand(TARGET_TYPE); 208 209 with(WorkingDir(configs["gamePath"].str)) 210 { 211 cleanAppleOSLibFolder(); 212 ProjectDetails d; 213 if(waitRedub(t, DubArguments(). 214 command("build").configuration("ios").arch(getIosTriple(arch, isSimulator)).compiler("ldc2").opts(cOpts), 215 d, 216 getHipPath("build", "appleos", XCodeDFolder, "libs")) != 0) 217 { 218 t.writelnError("Could not build for AppleOS."); 219 return ChoiceResult.Error; 220 } 221 string clean = appleClean ? "clean " : ""; 222 223 with(WorkingDir(getHipPath("build", "appleos"))) 224 { 225 t.wait(spawnShell( 226 "xcodebuild -jobs 8 -configuration Debug -scheme 'HipremeEngine iOS' " ~ 227 clean ~ 228 "build CONFIGURATION_BUILD_DIR=\"bin\" "~ 229 codeSignCommand ~ extraCommands ~ 230 getDestination() 231 )); 232 233 string device = `"`~configs["iosDevice"].str~`"`; 234 t.wait(spawnShell( 235 "xcrun simctl bootstatus " ~ device~ " -b || xcrun simctl boot " ~ device ~ 236 "&& open -a Simulator && "~ 237 "xcrun simctl install booted " ~ getHipPath("build", "appleos", "bin", "HipremeEngine.app") ~ " && " ~ 238 "xcrun simctl launch --console booted hipreme.HipremeEngine" 239 )); 240 } 241 } 242 if(configs["firstiOSRun"].boolean) 243 { 244 configs["firstiOSRun"] = false; 245 updateConfigFile(); 246 } 247 return ChoiceResult.None; 248 }